package cn.sc.summer.calculate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.sc.summer.calculate.annotation.JobExecution;
import cn.sc.summer.calculate.enums.TaskBehaviorEnum;
import cn.sc.summer.calculate.enums.TaskStatusEnum;
import cn.sc.summer.calculate.form.*;
import cn.sc.summer.calculate.manager.QuartzManager;
import cn.sc.summer.calculate.mapper.SysTaskLogMapper;
import cn.sc.summer.calculate.mapper.SysTaskMapper;
import cn.sc.summer.calculate.po.SysTask;
import cn.sc.summer.calculate.po.SysTaskLog;
import cn.sc.summer.calculate.service.TaskService;
import cn.sc.summer.calculate.vo.DefinedTaskInfoVO;
import cn.sc.summer.calculate.vo.TaskInfoVO;
import cn.sc.summer.constant.model.PageResult;
import cn.sc.summer.constant.util.AssertUtil;
import cn.sc.summer.exception.enums.CommonExceptionEnum;
import cn.sc.summer.exception.exceptions.CommonException;
import cn.sc.summer.mybatis.annotation.dynamicdatasource.DataSource;
import cn.sc.summer.mybatis.enums.DataSourceEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronExpression;
import org.quartz.Job;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 类名：定时任务业务层
 *
 * @author a-xin
 * @date 2024/2/4 13:51
 */
@Slf4j
@Service
public class TaskServiceImpl implements TaskService {

    @Resource
    private SysTaskMapper sysTaskMapper;

    @Resource
    private QuartzManager quartzManager;

    @Resource
    private SysTaskLogMapper sysTaskLogMapper;

    /**
     * 初始化定时任务信息
     */
    @Override
    public void initSchedule() {
        List<SysTask> sysTasks = sysTaskMapper.selectList(new LambdaQueryWrapper<SysTask>()
                .eq(SysTask::getJobStatus, TaskStatusEnum.RUNNING.getType()));
        AssertUtil.functionIfPre(sysTasks, CollUtil::isNotEmpty, () -> sysTasks.stream().forEach(sysTask -> {
            log.info("==> This scheduled task: {} will be initialized immediately", sysTask.getJobName());
            try {
                quartzManager.addJob(sysTask);
            } catch (Exception e) {
                throw new CommonException(e.getMessage(), e);
            }
        }));
    }

    /**
     * 更新定时任务
     *
     * @param form 任务信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTask(UpdateTaskForm form) {
        Optional.ofNullable(sysTaskMapper.selectById(form.getTaskId())).ifPresent(sysTask -> {
            sysTask.setCronExpression(form.getCorn());
            sysTaskMapper.updateById(sysTask);
            try {
                quartzManager.updateJobCron(sysTask);
            } catch (SchedulerException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 操作定时任务
     *
     * @param form 任务信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void operationTask(OperationTaskForm form) {

        TaskBehaviorEnum taskBehavior = Optional.ofNullable(TaskBehaviorEnum.getByType(form.getTaskBehavior()))
                .orElseThrow(() -> new CommonException("不存在对应的任务执行方式！"));
        Optional.ofNullable(sysTaskMapper.selectById(form.getTaskId())).ifPresent(sysTask -> {
            try {
                switch (taskBehavior) {
                    case PAUSE:
                        checkTaskStatus(sysTask);
                        quartzManager.pauseJob(sysTask);
                        break;
                    case START:
                        checkTaskStatus(sysTask);
                        quartzManager.runJobNow(sysTask);
                        break;
                    case DELETE:
                        checkTaskStatus(sysTask);
                        quartzManager.deleteJob(sysTask);
                        break;
                    case RESUME:
                        checkTaskStatus(sysTask);
                        quartzManager.resumeJob(sysTask);
                        break;
                    case ADD:
                        quartzManager.addJob(sysTask);
                        break;
                    case RUN_ONCE:
                        checkTaskStatus(sysTask);
                        quartzManager.runJobOnce(sysTask);
                        break;
                }
            } catch (Exception e) {
                throw new CommonException(e.getMessage(), e);
            }
            sysTask.setJobStatus(taskBehavior.getTaskStatus().getType());
            sysTaskMapper.updateById(sysTask);
        });

    }

    /**
     * 新增定时任务
     *
     * @param form 任务信息
     * @return 任务信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TaskInfoVO addTaskInfo(TaskInfoForm form) {
        checkTaskInfo(form);
        SysTask sysTask = new SysTask();
        BeanUtil.copyProperties(form, sysTask);
        sysTask.setId(IdWorker.getIdStr());
        sysTaskMapper.insert(sysTask);
        AssertUtil.functionIf(form.getJobStatus() != null, () -> {
            try {
                quartzManager.addJob(sysTask);
                AssertUtil.functionIf(form.getJobStatus().equals(TaskStatusEnum.RUNNING.getType()), () -> {
                    try {
                        quartzManager.runJobNow(sysTask);
                    } catch (SchedulerException e) {
                        throw new CommonException(e.getMessage(), e);
                    }
                });
            } catch (Exception e) {
                throw new CommonException(e.getMessage(), e);
            }
        });
        return BeanUtil.toBean(sysTask, TaskInfoVO.class);
    }

    /**
     * 更新定时任务
     *
     * @param form 任务信息
     * @return 任务信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TaskInfoVO updateTaskInfo(TaskInfoForm form) {
        checkTaskInfo(form);
        SysTask sysTask = Optional.ofNullable(sysTaskMapper.selectById(form.getId()))
                .orElseThrow(() -> new CommonException("不存在对应的定时任务信息！"));
        try {
            quartzManager.deleteJob(sysTask);
            BeanUtil.copyProperties(form, sysTask);
            sysTaskMapper.updateById(sysTask);
            quartzManager.addJob(sysTask);
            if (sysTask.getJobStatus().equals(TaskStatusEnum.RUNNING.getType())) {
                quartzManager.runJobNow(sysTask);
            }
        } catch (Exception e) {
            throw new CommonException(e.getMessage(), e);
        }
        return BeanUtil.toBean(sysTask, TaskInfoVO.class);
    }

    /**
     * 删除定时任务
     *
     * @param taskId 任务ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTaskInfo(String taskId) {
        Optional.ofNullable(sysTaskMapper.selectById(taskId))
                .ifPresent(sysTask -> sysTaskMapper.deleteById(taskId));
    }

    /**
     * 分页获取定时任务
     *
     * @param form 任务分页信息
     * @return 分页定时任务
     */
    @Override
    public PageResult<TaskInfoVO> getTaskInfo(GetTaskInfoForm form) {
        return sysTaskMapper.selectPage(form, new LambdaQueryWrapper<SysTask>()
                .eq(form.getJobStatus() != null, SysTask::getJobStatus, form.getJobStatus())
                .eq(StrUtil.isNotBlank(form.getJobGroup()), SysTask::getJobGroup, form.getJobGroup())
                .like(StrUtil.isNotBlank(form.getJobId()), SysTask::getId, form.getJobId())
                .like(StrUtil.isNotBlank(form.getJobName()), SysTask::getJobName, form.getJobName()), TaskInfoVO.class);
    }

    /**
     * 分页获取已定义的定时任务
     *
     * @param form 任务分页信息
     * @return 已定义定时任务
     */
    @Override
    public PageResult<DefinedTaskInfoVO> getDefinedTaskInfo(DefinedTaskInfoForm form) {

        final String BASE_PACKAGE = "cn.sc.summer";
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(JobExecution.class));
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(StrUtil.isNotBlank(form.getPackagePrefix()) ? form.getPackagePrefix() : BASE_PACKAGE);

        List<BeanDefinition> filterList = AssertUtil.functionIfPre(form.getRemark(), StrUtil::isNotBlank, () ->
                candidateComponents.stream().filter(beanDefinition -> {
                    try {
                        Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
                        JobExecution annotation = clazz.getAnnotation(JobExecution.class);
                        return AssertUtil.functionIfPre(annotation, ObjectUtil::isNotNull, () -> {
                            String value = annotation.value();
                            return AssertUtil.functionIfPre(value, StrUtil::isNotBlank, () -> value.contains(form.getRemark()), Boolean.FALSE);
                        }, Boolean.FALSE);
                    } catch (Exception e) {
                        throw new CommonException(e.getMessage(), e);
                    }
                }).collect(Collectors.toList()), new ArrayList<>());

        AssertUtil.functionIfPre(filterList, CollUtil::isNotEmpty, () -> {
            candidateComponents.clear();
            candidateComponents.addAll(filterList);
        });

        PageResult<DefinedTaskInfoVO> definedTaskInfo = new PageResult<>();
        definedTaskInfo.setTotal(candidateComponents.size());
        definedTaskInfo.setSize(form.getSize())
                .setCurrent(Math.max(form.getCurrent(), 1));
        int start = (int) ((form.getCurrent() - 1) * form.getSize());

        List<BeanDefinition> beanDefinitions = CollectionUtil.sub(candidateComponents, Math.max(start, 0), (int) form.getSize(), 1);
        AssertUtil.functionIfPre(beanDefinitions, CollUtil::isNotEmpty, () -> {
            List<DefinedTaskInfoVO> definedTaskInfoVOS = new ArrayList<>();
            beanDefinitions.stream().forEach(beanDefinition -> {
                        DefinedTaskInfoVO definedTaskInfoVO = new DefinedTaskInfoVO();
                        try {
                            Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
                            JobExecution annotation = clazz.getAnnotation(JobExecution.class);
                            if (annotation != null) {
                                String value = annotation.value();
                                definedTaskInfoVO.setPackageName(clazz.getName());
                                definedTaskInfoVO.setRemark(value);
                                definedTaskInfoVOS.add(definedTaskInfoVO);
                            }
                        } catch (Exception e) {
                            throw new CommonException(e.getMessage(), e);
                        }
                    }
            );
            definedTaskInfo.setRecords(definedTaskInfoVOS);
        }, () -> definedTaskInfo.setRecords(new ArrayList<>()));

        return definedTaskInfo;
    }

    /**
     * 保存定时任务日志信息
     *
     * @param sysTaskLog 执行日志信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @DataSource(DataSourceEnum.POST_GRE_SQL)
    public void saveSysTaskLog(SysTaskLog sysTaskLog) {
        sysTaskLogMapper.insert(sysTaskLog);
    }

    /**
     * 根据执行方法获取执行任务信息
     *
     * @param beanClass 执行日志方法
     */
    @Override
    public SysTask getSysTaskInfoByBeanClass(String beanClass) {
        return sysTaskMapper.selectOne(new LambdaQueryWrapper<SysTask>()
                .eq(SysTask::getBeanClass, beanClass));
    }

    /**
     * 校验任务信息是否合理
     *
     * @param form 信息表单数据
     * @param <T>  校验泛型
     */
    private <T extends TaskInfoForm> void checkTaskInfo(T form) {
        try {
            Class<?> clazz = Class.forName(form.getBeanClass());
            CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(!Job.class.isAssignableFrom(clazz), "{}: 该类不是定时任务继承类！", clazz);
            CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(sysTaskMapper.selectCount(new LambdaQueryWrapper<SysTask>()
                    .eq(SysTask::getJobName, form.getJobName())
                    .or()
                    .eq(SysTask::getBeanClass, form.getBeanClass())) > 0, "已存在相同名称或者相同定时任务类！");
            new CronExpression(form.getCronExpression());
        } catch (Exception e) {
            throw new CommonException(e.getMessage(), e);
        }
    }

    /**
     * 校验定时任务状态信息
     *
     * @param sysTask 定时任务信息
     * @param <T>     任务泛型
     */
    private <T extends SysTask> void checkTaskStatus(T sysTask) {
        CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(sysTask.getJobStatus().equals(TaskStatusEnum.DELETE.getType()), "该任务已被删除！");
    }

}
